﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.ServiceModel;
using System.Threading;
using System.Net;
using LitJson;
using System.Diagnostics;

namespace MyWcfRestLib
{
    abstract public class WcfService
    {
        protected int Port { get; set; }

        protected WebServiceHost ServiceHost { get; set; }

        public Uri BaseAddress { get; private set; }

        abstract protected string ServiceName { get; }

        abstract protected Type ServiceType { get; }

        public WcfService(int port)
        {
            this.Port = port;
        }

        public ServiceConnectionInfo ConnectionInfo { get; protected set; }
        public string ConnectionInfoJson { get { return JsonMapper.ToJson(ConnectionInfo); } }

        /// <summary>
        /// Adds and config the service.
        /// Will be implemented by derived class
        /// </summary>
        abstract protected void AddTheService();

        virtual public void Start()
        {
            string address = String.Format("http://localhost:{0}", Port);
            BaseAddress = new Uri(new Uri(address), ServiceName);
            ServiceHost = new WebServiceHost(ServiceType, BaseAddress);

            string hostName = Dns.GetHostName();
            IPAddress[] IPAddresses = Dns.GetHostAddresses(hostName);
            string[] ipList = IPAddresses.Where(item => item.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Select(item => item.ToString()).ToArray();
            ConnectionInfo = new ServiceConnectionInfo
            {
                ServicePort = Port,
                HostName = hostName,
                Addresses = ipList,
                ServiceName = this.ServiceName
            };
            string jsonString = JsonMapper.ToJson(ConnectionInfo);
            ServiceConnectionInfo newInfo = JsonMapper.ToObject<ServiceConnectionInfo>(jsonString);
            Debug.WriteLine(newInfo.HostName);

            try
            {

                // Check to see if the service host already has a ServiceMetadataBehavior
                ServiceMetadataBehavior smb = ServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
                // If not, add one
                if (smb == null)
                    smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;


                ServiceHost.Description.Behaviors.Add(smb);

                // Add MEX endpoint
                ServiceHost.AddServiceEndpoint(
                  ServiceMetadataBehavior.MexContractName,
                  MetadataExchangeBindings.CreateMexHttpBinding(),
                  "mex"
                );

                // Config and add corresponding service, be implemented by derived class.
                AddTheService();

                ServiceHost.Open();
            }
            catch (CommunicationException cex)
            {
                Console.WriteLine("An exception occurred: {0}", cex.Message);
                ServiceHost.Abort();
            }

        }

        public void Close()
        {
            if (null == ServiceHost)
                return;

            if (ServiceHost.State == CommunicationState.Opened)
            {
                ServiceHost.Close();
            }
        }

    }
}
